package com.github.syncrepo;

import com.github.syncrepo.model.SyncConfig;
import com.github.syncrepo.service.GitSyncService;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Console;
import java.util.Scanner;

/**
 * Main application class for Git Repository Sync Tool (GitCode/GitHub/GitLab)
 */
public class RepoSyncApplication {
    private static final Logger logger = LoggerFactory.getLogger(RepoSyncApplication.class);

    public static void main(String[] args) {
        try {
            // Build configuration from arguments or interactive input
            SyncConfig config = buildConfiguration(args);

            if (config == null) {
                return;
            }

            // Execute synchronization
            GitSyncService syncService = new GitSyncService();
            syncService.syncRepository(config);

            System.out.println("\n✓ Synchronization completed successfully!");

        } catch (Exception e) {
            logger.error("Error during synchronization: {}", e.getMessage(), e);
            System.err.println("\n✗ Error: " + e.getMessage());
            System.exit(1);
        }
    }

    /**
     * Build configuration from command line arguments or interactive input
     */
    private static SyncConfig buildConfiguration(String[] args) {
        Options options = createOptions();
        CommandLineParser parser = new DefaultParser();
        HelpFormatter formatter = new HelpFormatter();

        try {
            CommandLine cmd = parser.parse(options, args);

            if (cmd.hasOption("help")) {
                printHelp(formatter, options);
                return null;
            }

            // If interactive mode or missing required options, use interactive input
            if (cmd.hasOption("interactive") || !hasRequiredOptions(cmd)) {
                return buildInteractiveConfig();
            }

            // Build from command line
            return buildConfigFromCommandLine(cmd);

        } catch (ParseException e) {
            System.err.println("Error parsing arguments: " + e.getMessage());
            printHelp(formatter, options);
            System.exit(1);
            return null;
        }
    }

    /**
     * Create command line options
     */
    private static Options createOptions() {
        Options options = new Options();

        options.addOption(Option.builder("sr")
                .longOpt("source-repo")
                .hasArg()
                .desc("Source repository URL (e.g., git@github.com:user/repo.git or https://gitcode.com/user/repo.git)")
                .build());

        options.addOption(Option.builder("sb")
                .longOpt("source-branch")
                .hasArg()
                .desc("Source branch/tag name (e.g., main or v1.0.0)")
                .build());

        options.addOption(Option.builder("st")
                .longOpt("source-type")
                .hasArg()
                .desc("Source reference type: BRANCH or TAG (default: BRANCH)")
                .build());

        options.addOption(Option.builder("tr")
                .longOpt("target-repo")
                .hasArg()
                .desc("Target repository URL (e.g., git@github.com:user/repo.git or https://gitcode.com/youruser/yourrepo.git)")
                .build());

        options.addOption(Option.builder("tb")
                .longOpt("target-branch")
                .hasArg()
                .desc("Target branch/tag name (e.g., synced-main or v1.0.0)")
                .build());

        options.addOption(Option.builder("tt")
                .longOpt("target-type")
                .hasArg()
                .desc("Target reference type: BRANCH or TAG (default: BRANCH)")
                .build());

        options.addOption(Option.builder("t")
                .longOpt("token")
                .hasArg()
                .desc("Git Personal Access Token (required for HTTP/HTTPS, optional for SSH)")
                .build());

        options.addOption(Option.builder("ssh")
                .longOpt("ssh-key")
                .hasArg()
                .desc("Path to SSH private key (e.g., ~/.ssh/id_rsa or ~/.ssh/id_ed25519)")
                .build());

        options.addOption(Option.builder("w")
                .longOpt("workdir")
                .hasArg()
                .desc("Working directory (default: temp directory)")
                .build());

        options.addOption(Option.builder("k")
                .longOpt("keep")
                .desc("Keep local copy after synchronization")
                .build());

        options.addOption(Option.builder("f")
                .longOpt("force")
                .desc("Force push/overwrite if target tag/branch already exists")
                .build());

        options.addOption(Option.builder("i")
                .longOpt("interactive")
                .desc("Interactive mode - prompt for all inputs")
                .build());

        options.addOption(Option.builder("h")
                .longOpt("help")
                .desc("Display help information")
                .build());

        return options;
    }

    /**
     * Check if all required options are present
     * Token is optional for SSH URLs
     */
    private static boolean hasRequiredOptions(CommandLine cmd) {
        boolean hasBasicOptions = cmd.hasOption("source-repo") &&
               cmd.hasOption("source-branch") &&
               cmd.hasOption("target-repo") &&
               cmd.hasOption("target-branch");
        
        if (!hasBasicOptions) {
            return false;
        }
        
        // Check if SSH URLs are used
        String sourceRepo = cmd.getOptionValue("source-repo", "");
        String targetRepo = cmd.getOptionValue("target-repo", "");
        boolean isSsh = sourceRepo.startsWith("git@") || sourceRepo.startsWith("ssh://") ||
                       targetRepo.startsWith("git@") || targetRepo.startsWith("ssh://");
        
        // Token is required for HTTP/HTTPS URLs, optional for SSH
        return isSsh || cmd.hasOption("token");
    }

    /**
     * Build configuration from command line
     */
    private static SyncConfig buildConfigFromCommandLine(CommandLine cmd) {
        SyncConfig config = new SyncConfig();

        config.setSourceRepoUrl(cmd.getOptionValue("source-repo"));
        config.setSourceBranch(cmd.getOptionValue("source-branch"));
        config.setTargetRepoUrl(cmd.getOptionValue("target-repo"));
        config.setTargetBranch(cmd.getOptionValue("target-branch"));
        config.setAccessToken(cmd.getOptionValue("token"));
        config.setSshKeyPath(cmd.getOptionValue("ssh-key"));
        config.setWorkDir(cmd.getOptionValue("workdir"));
        config.setKeepLocalCopy(cmd.hasOption("keep"));
        config.setForceSync(cmd.hasOption("force"));

        // Parse ref types
        String sourceTypeStr = cmd.getOptionValue("source-type", "BRANCH");
        String targetTypeStr = cmd.getOptionValue("target-type", "BRANCH");
        
        try {
            config.setSourceRefType(SyncConfig.RefType.valueOf(sourceTypeStr.toUpperCase()));
            config.setTargetRefType(SyncConfig.RefType.valueOf(targetTypeStr.toUpperCase()));
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid ref type. Must be BRANCH or TAG.");
            System.exit(1);
        }

        return config;
    }

    /**
     * Build configuration interactively
     */
    private static SyncConfig buildInteractiveConfig() {
        System.out.println("\n=== Git Repository Sync Tool - Interactive Mode ===\n");

        Scanner scanner = new Scanner(System.in);

        System.out.print("Source repository URL: ");
        String sourceRepo = scanner.nextLine().trim();

        System.out.print("Source reference type (BRANCH/TAG) [BRANCH]: ");
        String sourceTypeStr = scanner.nextLine().trim();
        if (sourceTypeStr.isEmpty()) {
            sourceTypeStr = "BRANCH";
        }
        SyncConfig.RefType sourceType;
        try {
            sourceType = SyncConfig.RefType.valueOf(sourceTypeStr.toUpperCase());
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid type, using BRANCH as default");
            sourceType = SyncConfig.RefType.BRANCH;
        }

        System.out.print("Source " + sourceType.toString().toLowerCase() + " name: ");
        String sourceBranch = scanner.nextLine().trim();

        System.out.print("Target repository URL: ");
        String targetRepo = scanner.nextLine().trim();

        System.out.print("Target reference type (BRANCH/TAG) [BRANCH]: ");
        String targetTypeStr = scanner.nextLine().trim();
        if (targetTypeStr.isEmpty()) {
            targetTypeStr = "BRANCH";
        }
        SyncConfig.RefType targetType;
        try {
            targetType = SyncConfig.RefType.valueOf(targetTypeStr.toUpperCase());
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid type, using BRANCH as default");
            targetType = SyncConfig.RefType.BRANCH;
        }

        System.out.print("Target " + targetType.toString().toLowerCase() + " name: ");
        String targetBranch = scanner.nextLine().trim();

        // Check if SSH URLs are used
        boolean isSsh = sourceRepo.startsWith("git@") || sourceRepo.startsWith("ssh://") ||
                       targetRepo.startsWith("git@") || targetRepo.startsWith("ssh://");

        // Try to read token securely (optional for SSH)
        String token = "";
        if (isSsh) {
            System.out.print("Access Token (press Enter to skip for SSH): ");
            token = scanner.nextLine().trim();
        } else {
            Console console = System.console();
            if (console != null) {
                char[] tokenChars = console.readPassword("Access Token (input hidden): ");
                token = new String(tokenChars);
            } else {
                System.out.print("Access Token: ");
                token = scanner.nextLine().trim();
            }
        }

        // SSH key path (optional)
        String sshKeyPath = "";
        if (isSsh) {
            System.out.print("SSH private key path (press Enter for default ~/.ssh/id_rsa or id_ed25519): ");
            sshKeyPath = scanner.nextLine().trim();
        }

        System.out.print("Working directory (press Enter for temp dir): ");
        String workDir = scanner.nextLine().trim();

        System.out.print("Keep local copy? (y/N): ");
        String keepStr = scanner.nextLine().trim().toLowerCase();
        boolean keep = keepStr.equals("y") || keepStr.equals("yes");

        System.out.print("Force push if target exists? (y/N): ");
        String forceStr = scanner.nextLine().trim().toLowerCase();
        boolean force = forceStr.equals("y") || forceStr.equals("yes");

        System.out.println();

        // Build configuration directly
        SyncConfig config = new SyncConfig();
        config.setSourceRepoUrl(sourceRepo);
        config.setSourceBranch(sourceBranch);
        config.setSourceRefType(sourceType);
        config.setTargetRepoUrl(targetRepo);
        config.setTargetBranch(targetBranch);
        config.setTargetRefType(targetType);
        config.setAccessToken(token);
        config.setSshKeyPath(sshKeyPath.isEmpty() ? null : sshKeyPath);
        config.setWorkDir(workDir.isEmpty() ? null : workDir);
        config.setKeepLocalCopy(keep);
        config.setForceSync(force);

        return config;
    }

    /**
     * Print help information
     */
    private static void printHelp(HelpFormatter formatter, Options options) {
        System.out.println("\n=== Git Repository Sync Tool ===\n");
        System.out.println("This tool synchronizes a branch from a source repository to a target repository.\n");
        System.out.println("Supports: GitCode, GitHub, GitLab, and other Git hosting platforms.\n");
        
        formatter.printHelp("java -jar sync-repo.jar [OPTIONS]", "\nOptions:", options, 
                "\nExamples:\n" +
                "  Interactive mode:\n" +
                "    java -jar sync-repo.jar -i\n\n" +
                "  Sync branch to branch (SSH - recommended for large repos):\n" +
                "    java -jar sync-repo.jar \\\n" +
                "      --source-repo git@github.com:user/source-repo.git \\\n" +
                "      --source-branch main \\\n" +
                "      --source-type BRANCH \\\n" +
                "      --target-repo git@gitcode.com:youruser/target-repo.git \\\n" +
                "      --target-branch synced-main \\\n" +
                "      --target-type BRANCH\n\n" +
                "  Sync with custom SSH key:\n" +
                "    java -jar sync-repo.jar \\\n" +
                "      --source-repo git@github.com:user/repo.git \\\n" +
                "      --source-branch main \\\n" +
                "      --target-repo git@gitcode.com:user/repo.git \\\n" +
                "      --target-branch main \\\n" +
                "      --ssh-key ~/.ssh/my_custom_key\n\n" +
                "  Sync branch to branch (HTTPS with token):\n" +
                "    java -jar sync-repo.jar \\\n" +
                "      --source-repo https://gitcode.com/source/repo.git \\\n" +
                "      --source-branch main \\\n" +
                "      --source-type BRANCH \\\n" +
                "      --target-repo https://gitcode.com/your/repo.git \\\n" +
                "      --target-branch synced-main \\\n" +
                "      --target-type BRANCH \\\n" +
                "      --token YOUR_ACCESS_TOKEN\n\n" +
                "  Sync tag to tag (SSH):\n" +
                "    java -jar sync-repo.jar \\\n" +
                "      --source-repo git@github.com:user/repo.git \\\n" +
                "      --source-branch v1.0.0 \\\n" +
                "      --source-type TAG \\\n" +
                "      --target-repo git@gitcode.com:user/repo.git \\\n" +
                "      --target-branch v1.0.0 \\\n" +
                "      --target-type TAG\n\n" +
                "  Force overwrite existing tag (SSH):\n" +
                "    java -jar sync-repo.jar \\\n" +
                "      --source-repo git@github.com:user/repo.git \\\n" +
                "      --source-branch v1.0.0 \\\n" +
                "      --source-type TAG \\\n" +
                "      --target-repo git@gitcode.com:user/repo.git \\\n" +
                "      --target-branch v1.0.0 \\\n" +
                "      --target-type TAG \\\n" +
                "      --force\n\n" +
                "Authentication Methods:\n" +
                "  1. SSH (recommended for large repositories):\n" +
                "     - Use SSH URLs: git@host:user/repo.git\n" +
                "     - Requires SSH keys configured on your system (~/.ssh/id_rsa or id_ed25519)\n" +
                "     - No token needed, uses SSH key authentication\n" +
                "     - Test connection: ssh -T git@github.com\n\n" +
                "  2. HTTPS with Personal Access Token:\n" +
                "     - Use HTTPS URLs: https://host/user/repo.git\n" +
                "     - Requires personal access token with repo permissions\n" +
                "     - Generate token from:\n" +
                "       * GitCode: https://gitcode.com/-/user_settings/personal_access_tokens\n" +
                "       * GitHub: https://github.com/settings/tokens\n" +
                "       * GitLab: https://gitlab.com/-/user_settings/personal_access_tokens\n", 
                true);
    }
}

